#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
老年人护理服务调度系统
基于联邦学习的动态调度算法
"""

import json
import random
import uuid
from datetime import datetime, timedelta
from dataclasses import dataclass, asdict
from typing import List, Dict, Optional, Tuple
import math

# 服务类型枚举
class ElderlyCareServiceType:
    NURSING_HOME = "nursing_home"
    DAY_CARE = "day_care"
    HOME_NURSING = "home_nursing"
    HOME_BATHING = "home_bathing"
    HOME_REHABILITATION = "home_rehabilitation"
    HOSPITAL_ESCORT = "hospital_escort"
    ECO_THERAPY = "eco_therapy"
    CULTURAL_COMPANION = "cultural_companion"
    DIGITAL_COMPANION = "digital_companion"

# 紧急程度
class UrgencyLevel:
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"

# 需求状态
class DemandStatus:
    PENDING = "pending"
    MATCHED = "matched"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"

# 生态评分
@dataclass
class EcoScores:
    environmental: float  # 环境友好度 (0-100)
    social: float         # 社会责任感 (0-100)
    economic: float       # 经济可持续性 (0-100)
    
    def average(self) -> float:
        return (self.environmental + self.social + self.economic) / 3

# 位置坐标
@dataclass
class Location:
    latitude: float
    longitude: float
    
    def distance_to(self, other: 'Location') -> float:
        """计算两点间的距离（公里）"""
        lat1, lon1 = math.radians(self.latitude), math.radians(self.longitude)
        lat2, lon2 = math.radians(other.latitude), math.radians(other.longitude)
        
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        
        a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
        c = 2 * math.asin(math.sqrt(a))
        
        # 地球半径（公里）
        earth_radius = 6371
        return earth_radius * c

# 服务提供者
@dataclass
class CareProvider:
    id: str
    name: str
    service_type: str
    location: Location
    capacity: int
    available_hours: List[Tuple[int, int]]  # [(start_hour, end_hour)]
    rating: float
    certifications: List[str]
    eco_scores: EcoScores
    hourly_rate: Optional[float]
    description: str
    
    def is_available(self, start_time: datetime, duration_hours: int) -> bool:
        """检查在指定时间是否可用"""
        hour = start_time.hour
        for start, end in self.available_hours:
            if start <= hour < end:
                return True
        return False

# 老人需求
@dataclass
class ElderlyDemand:
    id: str
    elder_id: str
    service_type: str
    urgency: str
    location: Location
    start_time: datetime
    end_time: Optional[datetime]
    special_requirements: List[str]
    preferred_eco_scores: Optional[EcoScores]
    matched_provider: Optional[CareProvider] = None
    status: str = DemandStatus.PENDING
    created_at: datetime = None
    budget: Optional[float] = None
    
    def __post_init__(self):
        if self.created_at is None:
            self.created_at = datetime.now()

# 调度规则
@dataclass
class DispatchRule:
    region_id: str
    service_type_weights: Dict[str, float]
    priority_factors: Dict[str, float]
    eco_weight: float
    distance_weight: float
    rating_weight: float
    capacity_weight: float

# 联邦学习模型
class FederatedLearningModel:
    """联邦学习模型，用于生成调度规则"""
    
    def __init__(self):
        self.global_weights = {
            'distance': 0.3,
            'rating': 0.25,
            'eco_score': 0.2,
            'capacity': 0.15,
            'urgency': 0.1
        }
    
    def update_weights(self, local_updates: List[Dict[str, float]]):
        """聚合本地更新到全局权重"""
        if not local_updates:
            return
        
        # 简单的联邦平均算法
        avg_weights = {}
        for key in self.global_weights:
            values = [update.get(key, 0) for update in local_updates]
            avg_weights[key] = sum(values) / len(values) if values else self.global_weights[key]
        
        self.global_weights = avg_weights
    
    def get_weights(self) -> Dict[str, float]:
        return self.global_weights.copy()

# 调度系统核心
class ElderlyCareDispatchSystem:
    """老年人护理服务调度系统"""
    
    def __init__(self):
        self.providers: List[CareProvider] = []
        self.demands: List[ElderlyDemand] = []
        self.dispatch_rules: Dict[str, DispatchRule] = {}
        self.federated_model = FederatedLearningModel()
        self.stats = {
            'total_demands': 0,
            'matched_demands': 0,
            'completed_demands': 0,
            'average_match_time': 0
        }
    
    def add_provider(self, provider: CareProvider):
        """添加服务提供者"""
        self.providers.append(provider)
    
    def add_demand(self, demand: ElderlyDemand):
        """添加老人需求"""
        self.demands.append(demand)
        self.stats['total_demands'] += 1
    
    def generate_dispatch_rule(self, region_id: str) -> DispatchRule:
        """基于联邦学习生成调度规则"""
        weights = self.federated_model.get_weights()
        
        return DispatchRule(
            region_id=region_id,
            service_type_weights={
                ElderlyCareServiceType.NURSING_HOME: 1.0,
                ElderlyCareServiceType.DAY_CARE: 1.2,
                ElderlyCareServiceType.HOME_NURSING: 1.5,
                ElderlyCareServiceType.HOME_BATHING: 1.3,
                ElderlyCareServiceType.HOME_REHABILITATION: 1.4,
                ElderlyCareServiceType.HOSPITAL_ESCORT: 1.8,
                ElderlyCareServiceType.ECO_THERAPY: 0.9,
                ElderlyCareServiceType.CULTURAL_COMPANION: 0.8,
                ElderlyCareServiceType.DIGITAL_COMPANION: 0.7
            },
            priority_factors={
                'urgency_high': 2.0,
                'urgency_medium': 1.5,
                'urgency_low': 1.0
            },
            eco_weight=weights['eco_score'],
            distance_weight=weights['distance'],
            rating_weight=weights['rating'],
            capacity_weight=weights['capacity']
        )
    
    def calculate_match_score(self, demand: ElderlyDemand, provider: CareProvider, rule: DispatchRule) -> float:
        """计算匹配分数"""
        score = 0.0
        
        # 距离分数（越近越好）
        distance = demand.location.distance_to(provider.location)
        distance_score = max(0, 1 - distance / 50)  # 50公里内有效
        score += distance_score * rule.distance_weight
        
        # 评分分数
        rating_score = provider.rating / 5.0
        score += rating_score * rule.rating_weight
        
        # 生态评分
        eco_score = provider.eco_scores.average() / 100.0
        score += eco_score * rule.eco_weight
        
        # 容量分数
        capacity_score = min(1.0, provider.capacity / 10.0)
        score += capacity_score * rule.capacity_weight
        
        # 紧急程度
        urgency_factor = rule.priority_factors.get(f'urgency_{demand.urgency}', 1.0)
        score *= urgency_factor
        
        # 服务类型权重
        service_weight = rule.service_type_weights.get(demand.service_type, 1.0)
        score *= service_weight
        
        return score
    
    def match_demands(self) -> List[Tuple[ElderlyDemand, CareProvider]]:
        """匹配需求和提供者"""
        matches = []
        
        for demand in self.demands:
            if demand.status != DemandStatus.PENDING:
                continue
            
            best_provider = None
            best_score = 0.0
            
            # 获取区域调度规则
            region_id = "BJ_01"  # 简化处理
            rule = self.generate_dispatch_rule(region_id)
            
            for provider in self.providers:
                # 检查服务类型匹配
                if provider.service_type != demand.service_type:
                    continue
                
                # 检查时间可用性
                if not provider.is_available(demand.start_time, 2):
                    continue
                
                # 检查预算
                if demand.budget and provider.hourly_rate:
                    if provider.hourly_rate > demand.budget:
                        continue
                
                # 计算匹配分数
                score = self.calculate_match_score(demand, provider, rule)
                
                if score > best_score:
                    best_score = score
                    best_provider = provider
            
            if best_provider and best_score > 0.7:  # 阈值
                demand.matched_provider = best_provider
                demand.status = DemandStatus.MATCHED
                matches.append((demand, best_provider))
                self.stats['matched_demands'] += 1
        
        return matches
    
    def get_statistics(self) -> Dict:
        """获取系统统计信息"""
        return {
            'total_demands': self.stats['total_demands'],
            'pending_demands': len([d for d in self.demands if d.status == DemandStatus.PENDING]),
            'matched_demands': self.stats['matched_demands'],
            'completed_demands': self.stats['completed_demands'],
            'providers_count': len(self.providers),
            'average_rating': sum(p.rating for p in self.providers) / len(self.providers) if self.providers else 0
        }

# 测试和演示
def create_sample_data(system: ElderlyCareDispatchSystem):
    """创建示例数据"""
    
    # 添加服务提供者
    providers = [
        CareProvider(
            id=str(uuid.uuid4()),
            name="阳光养老院",
            service_type=ElderlyCareServiceType.NURSING_HOME,
            location=Location(latitude=39.9042, longitude=116.4074),
            capacity=50,
            available_hours=[(8, 18)],
            rating=4.5,
            certifications=["养老护理员证", "医疗执业证"],
            eco_scores=EcoScores(environmental=85, social=90, economic=75),
            hourly_rate=200,
            description="专业养老院，提供全方位护理服务"
        ),
        CareProvider(
            id=str(uuid.uuid4()),
            name="爱心护理中心",
            service_type=ElderlyCareServiceType.HOME_NURSING,
            location=Location(latitude=39.9142, longitude=116.4174),
            capacity=20,
            available_hours=[(9, 17)],
            rating=4.8,
            certifications=["护理员证", "护士执业证"],
            eco_scores=EcoScores(environmental=90, social=85, economic=80),
            hourly_rate=150,
            description="专业上门护理服务"
        ),
        CareProvider(
            id=str(uuid.uuid4()),
            name="康复之家",
            service_type=ElderlyCareServiceType.HOME_REHABILITATION,
            location=Location(latitude=39.9242, longitude=116.4274),
            capacity=15,
            available_hours=[(10, 16)],
            rating=4.6,
            certifications=["康复治疗师证", "医师执业证"],
            eco_scores=EcoScores(environmental=80, social=88, economic=85),
            hourly_rate=180,
            description="专业康复治疗和训练"
        ),
        CareProvider(
            id=str(uuid.uuid4()),
            name="陪诊天使",
            service_type=ElderlyCareServiceType.HOSPITAL_ESCORT,
            location=Location(latitude=39.9342, longitude=116.4374),
            capacity=30,
            available_hours=[(8, 18)],
            rating=4.7,
            certifications=["陪诊师证", "医疗知识培训证"],
            eco_scores=EcoScores(environmental=75, social=95, economic=70),
            hourly_rate=120,
            description="专业医院陪诊服务"
        )
    ]
    
    for provider in providers:
        system.add_provider(provider)
    
    # 添加老人需求
    demands = [
        ElderlyDemand(
            id=str(uuid.uuid4()),
            elder_id="elder_001",
            service_type=ElderlyCareServiceType.HOME_NURSING,
            urgency=UrgencyLevel.MEDIUM,
            location=Location(latitude=39.9150, longitude=116.4180),
            start_time=datetime.now() + timedelta(days=1),
            end_time=None,
            special_requirements=["糖尿病护理", "血压监测"],
            preferred_eco_scores=EcoScores(environmental=80, social=85, economic=75),
            budget=160
        ),
        ElderlyDemand(
            id=str(uuid.uuid4()),
            elder_id="elder_002",
            service_type=ElderlyCareServiceType.HOSPITAL_ESCORT,
            urgency=UrgencyLevel.HIGH,
            location=Location(latitude=39.9350, longitude=116.4380),
            start_time=datetime.now() + timedelta(days=2),
            end_time=None,
            special_requirements=["心脏病史", "需要轮椅"],
            preferred_eco_scores=EcoScores(environmental=70, social=90, economic=80),
            budget=150
        ),
        ElderlyDemand(
            id=str(uuid.uuid4()),
            elder_id="elder_003",
            service_type=ElderlyCareServiceType.HOME_REHABILITATION,
            urgency=UrgencyLevel.LOW,
            location=Location(latitude=39.9250, longitude=116.4280),
            start_time=datetime.now() + timedelta(days=3),
            end_time=None,
            special_requirements=["中风康复", "语言训练"],
            preferred_eco_scores=EcoScores(environmental=85, social=80, economic=85),
            budget=200
        )
    ]
    
    for demand in demands:
        system.add_demand(demand)

def main():
    """主函数"""
    print("🌟 老年人护理服务调度系统启动")
    print("=" * 50)
    
    # 创建系统实例
    system = ElderlyCareDispatchSystem()
    
    # 创建示例数据
    create_sample_data(system)
    
    # 显示初始统计
    stats = system.get_statistics()
    print(f"\n📊 系统初始状态：")
    print(f"   服务提供者：{stats['providers_count']} 个")
    print(f"   老人需求：{stats['total_demands']} 个")
    print(f"   平均评分：{stats['average_rating']:.1f} 分")
    
    # 执行匹配
    print("\n🔄 开始智能匹配...")
    matches = system.match_demands()
    
    # 显示匹配结果
    print(f"\n✅ 匹配完成！成功匹配 {len(matches)} 个需求")
    print("-" * 50)
    
    for i, (demand, provider) in enumerate(matches, 1):
        print(f"\n匹配 #{i}:")
        print(f"  👴 老人需求：{demand.service_type}")
        print(f"  📍 位置：({demand.location.latitude:.4f}, {demand.location.longitude:.4f})")
        print(f"  🏥 匹配服务：{provider.name}")
        print(f"  📊 匹配分数：{system.calculate_match_score(demand, provider, system.generate_dispatch_rule('BJ_01')):.2f}")
        print(f"  💰 费用：¥{provider.hourly_rate}/小时")
        print(f"  🌱 生态评分：{provider.eco_scores.average():.1f}")
    
    # 更新统计
    new_stats = system.get_statistics()
    print(f"\n📈 匹配后统计：")
    print(f"   待匹配需求：{new_stats['pending_demands']} 个")
    print(f"   已匹配需求：{new_stats['matched_demands']} 个")
    
    # 联邦学习权重展示
    weights = system.federated_model.get_weights()
    print(f"\n🧠 联邦学习权重：")
    for key, value in weights.items():
        print(f"   {key}: {value:.3f}")

if __name__ == "__main__":
    main()